<?php

class Lite_Collection_Bag
	extends Lite_Collection_AbstractCollection
 	implements Lite_Collection_MultiSet {

	/**
	 * @param LiteCollection $colA
	 * @param LiteCollection $colB
	 * @return LiteCollection
	 */
	static function union(LiteCollection $colA, LiteCollection $colB){
		return self::unionInto(new self, $colA, $colB);
	}
	
	/**
	 * La bolsa con los elementos
	 * @var array
	 */
	private $elements = array();

	/**
	 * 
	 */
	public function clear(){
		$this->elements = array();
	}

	/**
	 * @param mixed $element
	 */
	public function add($element){
		if(null === ($bagElement = $this->find($element))) {
			$this->elements[] = new xLite_Collection_BagElement($element);
			return;
		}
		$bagElement->add();
	}

	/**
	 * @param mixed $element
	 * @param int $count
	 */
	public function addMany($element, $count){
		if(null === ($bagElement = $this->find($element))) {
			$this->elements[] = new xLite_Collection_BagElement($element, $count);
			return;
		}
		$bagElement->add($count);
	}

	/**
	 * 
	 * @param mixed $anElement
	 */
	private function findIndex($anElement){
		foreach($this->elements as $index => $element) {
			if($element->is($anElement)) {
				return $index;
			}
		}
		return null;
	}

	/**
	 * 
	 */
	private function find($anElement){
		if(null !== ($index = $this->findIndex($anElement))) {
			return $this->elements[$index];
		}
	}

	/**
	 * @param mixed $element
	 */
	public function contains($element){
		return null !== $this->findIndex($element);
	}

	/**
	 * @param mixed $element
	 */
	public function remove($element){
		if(null !== ($bagElement = $this->find($element))) {
			$bagElement->remove(1);
			if( 0 === $bagElement->count() ) {
				array_slice($this->elements, $this->findIndex($element), 1);	
			}
		}
	}

	/**
	 * @return boolean
	 */
	public function isEmpty(){
		return empty($this->elements);
	}

	/**
	 * @return int
	 */
	public function size(){
		$size = 0;
		foreach($this->elements as $element) {
			$size += $element->count();
		}
		return $size;
	}

	/**
	 * @param mixed $element
	 * @return int
	 */
	public function count($element){
		if(null === ($bagElement = $this->find($element))) {
			return 0;
		}
		return $bagElement->count();
	}

	/**
	 * @return array
	 */
	public function toArray() {
		$arrayElements = array();
		foreach($this->elements as $element) {
			$c = $element->count();
			do {
				$elements[] = $element->get();
			} while(--$c > 0);
		}
		return $arrayElements;
	}
}

/**
 * @access private
 */
class xLite_Collection_BagElement {

	/**
	 * @var mixed
	 */
	private $element;

	/**
	 * @var int
	 */
	private $count;

	/**
	 * 
	 * @param mixed $element
	 * @param int $count
	 */
	function __construct($element, $count = 1){
		$this->element = $element;
		$this->count = $count;
	}

	/**
	 * @return mixed the element
	 */
	function get(){
		return $this->element;
	}

	/**
	 * @param mixed $element
	 * @return boolean
	 */
	function is($element){
		return $this->element === $element;
	}

	/**
	 */
	function add($count = 1){
		$this->count += $count;
	}
	
	/**
	 */
	function remove($count = 1){
		$this->count -= $count;
	}

	/**
	 * @return int
	 */
	function count(){
		return $this->count;
	}
}